﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using SpiceNet.Diagnostics;
using SpiceNet.Integration;

namespace SpiceNet.Circuits
{
    /// <summary>
    /// A class that represents the state of a circuit
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CircuitState
    {
        /// <summary>
        /// The current time point of simulation
        /// </summary>
        public double Time = 0.0;

        /// <summary>
        /// Gets the order of the matrix/vectors
        /// </summary>
        public int Order { get; private set; } = 0;

        /// <summary>
        /// Internal states that are used by devices
        /// </summary>
        public Vector<double>[] States { get; private set; } = null;

        /// <summary>
        /// The Yn-matrix used in the modified nodal analysis
        /// </summary>
        public Matrix<double> Matrix { get; private set; } = null;

        /// <summary>
        /// The RHS vector
        /// </summary>
        public Vector<double> Rhs { get; private set; } = null;

        /// <summary>
        /// The current solution
        /// </summary>
        public Vector<double> Solution { get; private set; } = null;

        /// <summary>
        /// The old solution vector - used for checking convergence
        /// </summary>
        public Vector<double> OldSolution { get; private set; } = null;

        /// <summary>
        /// Is the current circuit state initialized or not?
        /// </summary>
        public bool Initialized { get; private set; } = false;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="order"></param>
        /// <param name="maxintegrationorder"></param>
        public CircuitState() { }

        /// <summary>
        /// Initialize the state
        /// </summary>
        /// <param name="order"></param>
        /// <param name="states"></param>
        public void Init(Circuit ckt)
        {
            Order = ckt.Nodes.Count;
            Matrix = new SparseMatrix(Order + 1);
            Rhs = new SparseVector(Order + 1);
            Solution = new DenseVector(Order + 1);
            OldSolution = new DenseVector(Order + 1);

            // Allocate states
            States = new Vector<double>[ckt.Integration.MaxOrder + 2];
            int count = ckt.NumStates;
            if (ckt.NumStates == 0)
                count = 1;
            for (int i = 0; i < States.Length; i++)
                States[i] = new DenseVector(count);
            Initialized = true;
        }

        /// <summary>
        /// Destroy the current state
        /// </summary>
        public void Destroy()
        {
            Order = 0;
            Matrix = null;
            Rhs = null;
            States = null;
            Solution = null;
            OldSolution = null;
            Initialized = false;
        }

        /// <summary>
        /// Cycle the states
        /// </summary>
        public void CycleStates()
        {
            Vector<double> tmp = States[States.Length - 1];
            for (int i = States.Length - 2; i >= 0; i--)
                States[i + 1] = States[i];
            States[0] = tmp;
        }

        /// <summary>
        /// Solve the state matrix using the Rhs vector
        /// </summary>
        public void Solve()
        {
            // All indices at 0 are the ground node
            // We remove these rows/columns/items because they will lead to a singular matrix
            var m = Matrix.RemoveRow(0).RemoveColumn(0);
            var b = Rhs.SubVector(1, Rhs.Count - 1);

            // Create a new solution vector of the original size
            Solution = new DenseVector(Rhs.Count);

            // Fill the 1-N elements with the solution
            var sol = m.Solve(b);
            Solution.SetSubVector(1, Solution.Count - 1, sol);
        }

        /// <summary>
        /// Clears the matrix and Rhs vector
        /// </summary>
        public void ClearLoad()
        {
            Matrix.Clear();
            Rhs.Clear();
        }

        /// <summary>
        /// Swap the current solution and the new solution
        /// </summary>
        public void SwapSolutions()
        {
            var temp = OldSolution;
            OldSolution = Solution;
            Solution = temp;
        }
    }
}
